package com.wrupple.vegetate.server.bussiness.data;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.ConversionService;

import com.wrupple.muba.common.domain.FilterData;
import com.wrupple.vegetate.client.module.services.logic.FieldDescriptionService;
import com.wrupple.vegetate.domain.CatalogDescriptor;
import com.wrupple.vegetate.domain.CatalogObject;
import com.wrupple.vegetate.domain.exceptions.CatalogException;
import com.wrupple.vegetate.domain.exceptions.DataLayerException;
import com.wrupple.vegetate.server.bussiness.PMF;
import com.wrupple.vegetate.server.bussiness.engine.CatalogEngineConfiguration;
import com.wrupple.vegetate.server.data.CatalogEntryPersister;
import com.wrupple.vegetate.server.domain.CatalogRetrivePacket;
import com.wrupple.vegetate.server.domain.CatalogUpdatePacket;

public class JDOCatalogEntryPersister implements CatalogEntryPersister {
	/** Logger available to subclasses */
	protected final Log logger = LogFactory.getLog(getClass());
	private ConversionService conversionService;
	private FieldDescriptionService descriptor;

	private Object parseKeyString(String id) {
		try {
			return Long.parseLong(id);
		} catch (NumberFormatException e) {
			return id;
		}
	}

	public JDOCatalogEntryPersister() {
		super();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.wrupple.muba.catalogs.server.CatalogEntryPersister#readEntry(java
	 * .lang.String, com.wrupple.muba.catalogs.domain.CatalogDescriptor,
	 * boolean)
	 */
	@Override
	public CatalogRetrivePacket readEntry(String id, CatalogDescriptor descriptor) throws DataLayerException {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		CatalogObject originalEntry = null;
		Class<?> clazz = getCatalogClass(descriptor);
		Object temp = null;
		try {
			temp = pm.getObjectById(clazz, parseKeyString(id));
			originalEntry = (CatalogObject) pm.detachCopy(temp);
			BeanUtils.copyProperties(temp, originalEntry);

		} catch (JDOObjectNotFoundException notFound) {
			temp = null;
			originalEntry = null;
		} catch (Exception e) {
			throw new DataLayerException("Error retriving catalog entry", e, DataLayerException.UNKNOWN);
		} finally {
			pm.close();
		}

		return new CatalogRetrivePacket(originalEntry, null, descriptor);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.wrupple.muba.catalogs.server.CatalogEntryPersister#createEntry(java
	 * .io.Serializable, com.wrupple.muba.catalogs.domain.CatalogDescriptor)
	 */
	@Override
	public CatalogUpdatePacket createEntry(CatalogObject entry, CatalogDescriptor descriptor) throws DataLayerException {
		assert entry != null : "Attempted to create an empty Catalog Entry";
		PersistenceManager pm = PMF.get().getPersistenceManager();
		CatalogObject createdEntry = null;
		try {
			createdEntry = pm.makePersistent(entry);
			assert createdEntry != null : "Persistence manager returned null entry while attempting to create one";
		} catch (Exception e) {
			throw new DataLayerException("Error creating catalog entry", e, DataLayerException.UNKNOWN);
		} finally {
			pm.close();
		}

		CatalogUpdatePacket regreso = new CatalogUpdatePacket(entry, createdEntry, descriptor);
		return regreso;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.wrupple.muba.catalogs.server.CatalogEntryPersister#updateEntry(java
	 * .io.Serializable, java.lang.String,
	 * com.wrupple.muba.catalogs.domain.CatalogDescriptor)
	 */
	@Override
	public CatalogUpdatePacket updateEntry(CatalogObject entry, String id, CatalogDescriptor descriptor)
			throws DataLayerException {
		Class<?> clazz = getCatalogClass(descriptor);
		PersistenceManager pm = PMF.get().getPersistenceManager();
		CatalogObject updatedEntry = null;
		CatalogObject originalEntry = null;
		BeanWrapper ow;
		BeanWrapper ew;
		Set<String> fieldNames;
		try {
			// remove the original
			fieldNames = this.descriptor.getUpdateDescriptors(descriptor).keySet();
			updatedEntry = (CatalogObject) pm.getObjectById(clazz, parseKeyString(id));
			originalEntry = pm.detachCopy(updatedEntry);
			ow = new BeanWrapperImpl(updatedEntry);
			ow.setConversionService(this.conversionService);
			ew = new BeanWrapperImpl(entry);
			ew.setConversionService(this.conversionService);
			// copy values
			for (String desc : fieldNames) {
				ow.setPropertyValue(desc, ew.getPropertyValue(desc));
			}
			// persist the replacement
			pm.makePersistent(updatedEntry);
		} catch (Exception e) {
			throw new DataLayerException("Error Updating entry " + descriptor.getId() + " / " + id, e,
					DataLayerException.UNKNOWN);
		} finally {
			pm.close();
		}
		return new CatalogUpdatePacket(originalEntry, updatedEntry, descriptor);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.wrupple.muba.catalogs.server.CatalogEntryPersister#bulkDelete(java
	 * .util.List, com.wrupple.muba.catalogs.domain.CatalogDescriptor)
	 */
	@Override
	public boolean bulkDelete(List<String> ids, CatalogDescriptor descriptor) throws DataLayerException {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Class<?> clazz = getCatalogClass(descriptor);
		try {
			List<Serializable> entries = new ArrayList<Serializable>(ids.size());
			Serializable temp;
			for (String id : ids) {
				temp = (Serializable) pm.getObjectById(clazz, parseKeyString(id));
				entries.add(temp);
			}
			pm.deletePersistentAll(entries);
		} catch (Exception e) {
			throw new DataLayerException("Error deletinies catalog entry", e, DataLayerException.UNKNOWN);
		} finally {
			pm.close();
		}
		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.wrupple.muba.catalogs.server.CatalogEntryPersister#deleteEntry(java
	 * .io.Serializable, com.wrupple.muba.catalogs.domain.CatalogDescriptor)
	 */
	@Override
	public CatalogObject deleteEntry(CatalogObject entry, CatalogDescriptor descriptor) {
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.wrupple.muba.catalogs.server.CatalogEntryPersister#setConfiguration
	 * (com.wrupple.muba.catalogs.domain.CatalogEngineConfiguration)
	 */
	@Override
	public void setConfiguration(CatalogEngineConfiguration configuration) {
	}

	/**
	 * calls class for name for the given catalog's java class
	 * 
	 * @param catalog
	 * @return
	 * @throws DataLayerException
	 */
	private Class<?> getCatalogClass(CatalogDescriptor catalog) throws DataLayerException {
		Class<?> clazz = null;
		try {
			clazz = Class.forName(catalog.getClazz());
		} catch (ClassNotFoundException e) {
			throw new DataLayerException("Could not lod class name for catalog", e, DataLayerException.UNKNOWN);
		}
		return clazz;
	}

	public void setConversionService(ConversionService conversionService) {
		this.conversionService = conversionService;
	}


	@Autowired
	public void setDescriptor(FieldDescriptionService descriptor) {
		this.descriptor = descriptor;
	}

	@Override
	public List<CatalogObject> readEntries(CatalogDescriptor descriptor, FilterData filter) throws DataLayerException {
		if (descriptor == null) {
			return null;
		}
		PersistenceManager pm = PMF.get().getPersistenceManager();
		// PREPARE QUERY

		Query query = pm.newQuery("select from " + descriptor.getClazz());

		// ADD FILTERS

		PMF.setQueryCriteria(query, filter, null);

		// GET ENTRIES
		ArrayList<CatalogObject> regreso=null;
		CatalogObject instance;
		
		Class<? extends CatalogObject> clazz;
		try {
			clazz = (Class<? extends CatalogObject>) Class.forName(descriptor.getClazz());
		} catch (ClassNotFoundException e1) {
			throw new DataLayerException("Not Found", e1, CatalogException.MISSINGCLASS);
		}
		
		try {
			List<CatalogObject> results = (List<CatalogObject>) query.execute();
			if(!results.isEmpty()){
				regreso = new ArrayList<CatalogObject>(results.size());
				
				for(CatalogObject o : results){
					instance = BeanUtils.instantiate(clazz );
					BeanUtils.copyProperties(o, instance);
					regreso.add(instance);
				}
				
			}
			
		} catch (Exception e) {
			throw new DataLayerException("Error retriving entries", e, DataLayerException.UNKNOWN);
		}
		pm.close();
		return regreso;
	}

	@Override
	public List<CatalogObject> readEntries(List<String> ids, CatalogDescriptor catalogDescriptor) throws DataLayerException {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Class<? extends CatalogObject> clazz;
		try {
			clazz = (Class<? extends CatalogObject>) Class.forName(catalogDescriptor.getClazz());
		} catch (ClassNotFoundException e1) {
			throw new DataLayerException("Not Found", e1, CatalogException.MISSINGCLASS);
		}
		List<CatalogObject> regreso = new ArrayList<CatalogObject>(ids.size());
		
		CatalogObject temp ;
		for(String id : ids){
			temp = pm.getObjectById(clazz, id);
			regreso.add(temp);
		}
		
		pm.close();
		return regreso;
	}

}
